package com.sskj.api.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sskj.api.domain.Device;
import com.sskj.api.domain.ProjectDevice;
import com.sskj.api.domain.ThingModelTemplate;
import com.sskj.api.exception.LogicException;
import com.sskj.api.mapper.DeviceMapper;
import com.sskj.api.properties.AppProperties;
import com.sskj.api.properties.MotherboardProperties;
import com.sskj.api.properties.SRSProperties;
import com.sskj.api.service.IDeviceService;
import com.sskj.api.service.IProductService;
import com.sskj.api.service.IProjectDeviceService;
import com.sskj.api.vo.req.DeviceReq;
import com.sskj.common.constant.RedisConstant;
import com.sskj.common.dto.DeviceProperty;
import com.sskj.common.dto.DeviceShadow;
import com.sskj.common.dto.InputParam;
import com.sskj.common.enums.ApiResponseCodeEnum;
import com.sskj.common.enums.ThingModelDataTypeEnum;
import com.sskj.common.enums.ThingModelTypEnum;
import com.sskj.common.util.StrUtil;
import com.sskj.common.vo.ApiResponse;
import com.sskj.common.vo.req.DeviceCommandReq;
import com.sskj.common.vo.req.PushCameraStreamReq;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class DeviceService extends ServiceImpl<DeviceMapper, Device> implements IDeviceService {

    @Autowired
    private MotherboardProperties motherboardProperties;

    @Autowired
    private SRSProperties srsProperties;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private IProductService productService;

    @Autowired
    private AppProperties appProperties;

    @Autowired
    private IProjectDeviceService projectDeviceService;

    @Override
    @Transactional
    public void add(Device device) {
        LambdaQueryWrapper<Device> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Device::getCode, device.getCode());
        if (count(lambdaQueryWrapper) > 0) {
            throw new LogicException("设备编码已存在");
        }

        device.setCreateTime(LocalDateTime.now());
        device.setStatus(0);
        if(save(device)) {
            if (device.getProjectId() != null) {
                ProjectDevice projectDevice = new ProjectDevice();
                projectDevice.setDeviceId(device.getId());
                projectDevice.setProjectId(device.getProjectId());
                projectDeviceService.add(projectDevice);
            }
        }
    }

    @Override
    @Transactional
    public void deleteById(Long id) {
        removeById(id);
        projectDeviceService.deleteByDeviceId(id);
    }

    @Override
    @Transactional
    public void update(Device device) {
        Device oldDevice = getById(device.getId());
        if (oldDevice != null) {
            device.setCode(oldDevice.getCode());
            device.setUpdateTime(LocalDateTime.now());
            updateById(device);

            if (device.getProjectId() != null && !Objects.equals(oldDevice.getProjectId(), device.getProjectId())) {
                projectDeviceService.update(device.getProjectId(), device.getId());
            }
        }
    }

    @Override
    public IPage<Device> pageList(DeviceReq req) {
        IPage<Device> pageConfig = new Page<>(req.getPageNum(), req.getPageSize());
        return baseMapper.pageList(pageConfig, req);
    }

    @Override
    public Device detail(Long id) {
        return baseMapper.detail(id);
    }

    @Override
    public List<DeviceProperty> getDeviceProperties(Long id) {
        List<DeviceProperty> deviceProperties = new ArrayList<>();
        // 获取物模型定义
        Device device = getById(id);
        if (device != null) {
            List<ThingModelTemplate> thingModels = productService.getThingModels(device.getProductId());
            DeviceShadow deviceShadow = getDeviceShadow(device.getCode());
            if (thingModels != null && !thingModels.isEmpty()) {
                for (ThingModelTemplate template : thingModels) {
                    if (template.getType() == ThingModelTypEnum.PROPERTY.getType()) {
                        DeviceProperty deviceProperty = new DeviceProperty();
                        deviceProperty.setIdentifier(template.getIdentifier());
                        deviceProperty.setName(template.getName());
                        deviceProperty.setUnit(template.getPropUnit());
                        deviceProperty.setValue(getPropValueByDeviceShadow(template, deviceShadow));

                        deviceProperties.add(deviceProperty);
                    }
                }
            }
        }
        return deviceProperties;
    }

    @Override
    public List<com.sskj.common.dto.DeviceService> getDeviceServices(Long id) {
        List<com.sskj.common.dto.DeviceService> deviceServices = new ArrayList<>();
        // 获取物模型定义
        Device device = getById(id);
        if (device != null) {
            List<ThingModelTemplate> thingModels = productService.getThingModels(device.getProductId());
            if (thingModels != null && !thingModels.isEmpty()) {
                for (ThingModelTemplate template : thingModels) {
                    if (template.getType() == ThingModelTypEnum.SERVICE.getType()) {
                        com.sskj.common.dto.DeviceService deviceService = new com.sskj.common.dto.DeviceService();
                        deviceService.setName(template.getName());
                        deviceService.setIdentifier(template.getIdentifier());
                        deviceService.setInputParams(template.getServiceInputParams());

                        deviceServices.add(deviceService);
                    }
                }
            }
        }
        return deviceServices;
    }

    @Override
    public List<com.sskj.common.dto.DeviceService> getDeviceServices(String deviceCode) {
        LambdaQueryWrapper<Device> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Device::getCode, deviceCode);
        Device device = getOne(lambdaQueryWrapper);
        if (device != null) {
            return getDeviceServices(device.getId());
        }
        return List.of();
    }

    @Override
    public Object sendCommand(DeviceCommandReq req) {
        List<com.sskj.common.dto.DeviceService> deviceServices = getDeviceServices(req.getDeviceCode());
        if (deviceServices == null || deviceServices.isEmpty()) {
            throw new LogicException("不支持的设备指令");
        }
        // 查找物模型的事件定义
        com.sskj.common.dto.DeviceService deviceService = null;
        for (com.sskj.common.dto.DeviceService item : deviceServices) {
            if (item.getIdentifier().equals(req.getServiceId())) {
                deviceService = item;
                break;
            }
        }
        if (deviceService == null) {
            throw new LogicException("不支持的设备指令");
        } else {
            // 校验请求参数
            List<InputParam> requireInputParams = new ArrayList<>();
            List<InputParam> inputParams = deviceService.getInputParams();
            if (inputParams != null && !inputParams.isEmpty()) {
                requireInputParams = inputParams.stream().filter(InputParam::getRequired).toList();
            }

            if (!requireInputParams.isEmpty()) {
                for (InputParam requiredInputParam : requireInputParams) {
                    if (req.getParams() == null) {
                        throw new LogicException("指令参数[" + requiredInputParam.getName() + "]缺失");
                    } else {
                        Object o = req.getParams().get(requiredInputParam.getIdentifier());
                        if (o == null) {
                            throw new LogicException("指令参数[" + requiredInputParam.getName() + "]缺失");
                        }
                    }
                }
            }

            // 远程调用统一接入网关
            String url = appProperties.getGatewayUrl() + "gateway/internal/command";
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .connectTimeout(10, TimeUnit.SECONDS)
                    .readTimeout(30, TimeUnit.SECONDS)
                    .writeTimeout(30, TimeUnit.SECONDS)
                    .build();
            try {
                // 将 DeviceCommandReq 对象序列化为 JSON 字符串
                String jsonBody = objectMapper.writeValueAsString(req);

                // 构建 RequestBody
                RequestBody body = RequestBody.create(
                        jsonBody,
                        MediaType.get("application/json; charset=utf-8")
                );

                // 构建 Request
                Request request = new Request.Builder()
                        .url(url)
                        .post(body)
                        .addHeader("Content-Type", "application/json")
                        .build();

                try(Response response = okHttpClient.newCall(request).execute()) {
                    if (!response.isSuccessful()) {
                        log.error("[设备服务]-下发指令请求失败。失败信息code={}|message={}", response.code(), response.message());
                        throw new LogicException("设备命令下发失败。");
                    }

                    String responseBody = response.body() != null ? response.body().string() : "";
                    if (StrUtil.isBlank(responseBody)) {
                        throw new LogicException("设备命令下发响应失败");
                    } else {
                        ApiResponse apiResponse = objectMapper.readValue(responseBody, ApiResponse.class);
                        if (apiResponse.getCode() == 0) {
                            return apiResponse.getData();
                        } else {
                            throw new LogicException(apiResponse.getMessage());
                        }
                    }
                }
            } catch (IOException e) {
                log.error("远程调用异常: {}", e.getMessage(), e);
                throw new RuntimeException("设备命令下发失败: " + e.getMessage(), e);
            }
        }
    }

    /**
     * 获取正确属性值
     * @param template 物模型定义实例
     * @param deviceShadow 设备影子
     * @return 属性值
     */
    private Object getPropValueByDeviceShadow(ThingModelTemplate template, DeviceShadow deviceShadow) {
        Object objValue = null;
        if (deviceShadow != null) {
            Map<String, Object> properties = deviceShadow.getProperties();
            if (properties != null) {
                objValue = properties.get(template.getIdentifier());
            }
        }

        if (Objects.equals(template.getPropDataType(), ThingModelDataTypeEnum.INTEGER.getDataType())) {
            if (objValue == null) {
                return 0;
            } else {
                return Integer.parseInt(objValue.toString());
            }
        }
        if (Objects.equals(template.getPropDataType(), ThingModelDataTypeEnum.FLOAT.getDataType())) {
            if (objValue == null) {
                return 0L;
            } else {
                return Double.parseDouble(objValue.toString());
            }
        }
        if (Objects.equals(template.getPropDataType(), ThingModelDataTypeEnum.DATETIME.getDataType())) {
            if (objValue == null) {
                return "";
            } else {
                String objValueStr = objValue.toString();
                // 判断是否为纯数字（时间戳）
                if (objValueStr.matches("\\d+")) {
                    try {
                        long timestamp = Long.parseLong(objValueStr);

                        // 根据长度判断是秒级还是毫秒级时间戳
                        if (objValueStr.length() == 10) { // 秒级时间戳
                            timestamp *= 1000L;
                        } else if (objValueStr.length() != 13) { // 非毫秒也非秒，不处理
                            return objValueStr; // 原样返回s
                        }

                        // 格式化为 yyyy-MM-dd HH:mm:ss
                        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        sdf.setTimeZone(TimeZone.getDefault()); // 或指定时区，如 TimeZone.getTimeZone("GMT+8")
                        return sdf.format(new Date(timestamp));

                    } catch (NumberFormatException e) {
                        log.warn("[设备服务]-时间戳转换失败: {}", objValueStr, e);
                        return objValueStr; // 转换失败，原样返回
                    }
                }
            }
        }

        return null;
    }

    @Override
    public String playVideo(Long id, Integer channelNumber, Integer qualityType) {
        // 获取设备对应的标识
        String deviceCode = "20250702V101";
        PushCameraStreamReq pushCameraStreamReq = new PushCameraStreamReq();
        pushCameraStreamReq.setDeviceId(deviceCode);
        pushCameraStreamReq.setChannelNumber(channelNumber);
        pushCameraStreamReq.setVideoQualityType(qualityType);

        String url = motherboardProperties.getApiUrl() + "motherboard/ptz/pushCameraStream?deviceId=" + deviceCode + "&channelNumber=" + channelNumber + "&videoQualityType=" + qualityType;

        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url).build();
        String playUrl = "";
        try(Response response = okHttpClient.newCall(request).execute()) {
            if (response.isSuccessful() && response.body() != null) {
                String responseBody = response.body().string();
                log.info("推流的响应结果:{}-{}", responseBody, url);
                try {
                    ApiResponse apiResponse = objectMapper.readValue(responseBody, ApiResponse.class);
                    if (Objects.equals(apiResponse.getCode(), ApiResponseCodeEnum.SUCCESS.getCode())) {
                        return srsProperties.getRtmpPlayUrl() + deviceCode + "_" + channelNumber + "_" +qualityType;
                    }
                } catch (JsonProcessingException e) {
                    log.error("推流的响应结果反序列化异常。响应结果:{}|异常信息:{}", responseBody, e.getMessage());
                }
            } else {
                log.error("推流请求失败。异常代码: {}", response.code());
            }
        } catch (IOException e) {
            log.error("请求失败。异常信息:{}", e.getMessage());
        }
        if (StrUtil.isBlank(playUrl)) {
            throw new LogicException("播放失败");
        }
        return playUrl;
    }

    @Override
    public void stopVideo(Long id, Integer channelNumber) {
        // 获取设备对应的标识
        String deviceCode = "20250702V101";
        stopVideo(deviceCode, channelNumber);
    }

    @Override
    public void stopVideo(String code, Integer channel) {
        String url = motherboardProperties.getApiUrl() + "motherboard/ptz/stopCameraStream?deviceId=" + code + "&channelNumber=" + channel;
//        log.info("停止推流播放地址:{}", url);
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url(url).build();
        try (Response response = okHttpClient.newCall(request).execute()){
            if (!response.isSuccessful() || response.body() == null) {
                log.error("[停止视频推流]-请求响应异常。异常信息状态码:{}|请求地址:{}", response.code(), url);
            }
        } catch (IOException e) {
            log.error("[停止视频推流]-请求异常。异常信息:{}|请求地址:{}", e.getMessage(), url);
        }
    }

    @Override
    public Device getByCode(String code) {
        LambdaQueryWrapper<Device> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.eq(Device::getCode, code);
        return getOne(lambdaQueryWrapper);
    }

    @Override
    public IPage<Device> myDevices(Long userId, Long pageNo, Long pageSize) {
        IPage<Device> page = new Page<>(pageNo, pageSize);
        return baseMapper.myDevices(page, userId);
    }

    @Override
    public Long myDevices(Long userId) {
        return baseMapper.myDevicesCount(userId);
    }

    /**
     * 获取设备最新状态信息
     * @param code 设备编码
     * @return 信息集合
     */
    private DeviceShadow getDeviceShadow(String code) {
        String key = RedisConstant.DEVICE_SHADOW + code;
        String cacheData = stringRedisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(cacheData)) {
            try {
                return objectMapper.readValue(cacheData, DeviceShadow.class);
            } catch (JsonProcessingException e) {
                log.error("[设备服务]-获取最新的属性缓存信息反序列化异常。异常信息:{}|内容:{}", e.getMessage(), cacheData);
            }
        }
        return null;
    }
}
